package com.example.spring.proxy;

import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;

import java.util.Set;

public class ClassPathOpenapiScanner extends ClassPathBeanDefinitionScanner {

    public ClassPathOpenapiScanner(BeanDefinitionRegistry registry) {
        super(registry, false);
    }

    public void registerFilters() {
        addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
        addExcludeFilter(
                (metadataReader, metadataReaderFactory) -> {
                    String className = metadataReader.getClassMetadata().getClassName();
                    return className.endsWith("package-info");
                }
        );
    }

    /**
     * AttributeAccessor
     * AttributeAccessorSupport内部维护Map用于存储attribute，Map的存在是为了兼容后续Spring扩展。
     * 必不可少的属性都在BeanDefinition中定义了，如果BeanDefinition有新特性，不需要新增属性，在Map中维护即可
     * <p>
     * BeanMetadataElement
     * #Object getSource：
     * 返回FileSystemResource：file [/Users/yangzhen/code/openapi-sdk/openapi-sdk-sample/target/classes/com/example/openapi/tuya/OpenapiConfig.class]
     * 返回SimpleMethodMetadata：com.example.openapi.tuya.OpenapiConfig.accessorFactoryBean(openapi.sdk.spring.GlobalConfiguration)，这种针对的是@Bean配置的对象
     * <p>
     * ChildBeanDefinition从2.5开始废弃，由GenericBeanDefinition代替
     * 对比两者源码，ChildBeanDefinition定义的不优雅，并且从命名来看，此类有局限性。
     * GenericBeanDefinition更加灵活，既可以做父，又可以做子。
     * <p>
     * RootBeanDefinition
     * 可以作为其他BeanDefinition的父，也可以单独作为BeanDefinition，但不能作为子
     * 在mergedBeanDefinitions存储的都是RootBeanDefinition，获取到RootBeanDefinition
     * 从Spring2.5开始，RootBeanDefinition仅作为运行时的BeanDefinition视图。如果需要编程定义BeanDefinition，那么推荐使用GenericBeanDefinition。
     * Spring的解释是:GenericBeanDefinition的优势在于，它允许动态定义父依赖项，而不是一个以"硬编码"定义BeanDefinition的角色。
     * 也就是说，bean的一般形式是以GenericBeanDefinition的标准形式存在的，在特定的时机，会将GenericBeanDefinition转成RootBeanDefinition
     *
     * @param basePackages
     * @return
     */
    @Override
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);
        GenericBeanDefinition definition;
        for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
            definition = (GenericBeanDefinition) beanDefinitionHolder.getBeanDefinition();
            definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
            definition.setBeanClass(OpenapiFactoryBean.class);
        }

        return beanDefinitionHolders;
    }


    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        return true;
    }

}
